package net.midnighttd.engine.map;

/**
 * Author: Adam Scarr
 * Date: 18/04/2009
 * Time: 4:35:11 PM
 */
public class TileSignature {
    private int signature;
    private Tile tile;
    public static final int N  = 128;
    public static final int NE = 64;
    public static final int E  = 32;
    public static final int SE = 16;
    public static final int S  = 8;
    public static final int SW = 4;
    public static final int W  = 2;
    public static final int NW = 1;

    private class Direction {
        int x, y, bit;
        public Direction(int x, int y, int bit) {
            this.x = x;
            this.y = y;
            this.bit = bit;
        }
    }

    public TileSignature(Map map, int x, int y) {
        assert(map.inBounds(x,y));
        tile = map.get(x, y);
        Direction[] directions = {new Direction(0, -1, N),
                new Direction(1, -1, NE),
                new Direction(1, 0, E),
                new Direction(1, 1, SE),
                new Direction(0, 1, S),
                new Direction(-1, 1, SW),
                new Direction(-1, 0, W),
                new Direction(-1, -1, NW)};
        
        for(Direction d: directions) {
            if(map.inBounds(x + d.x, y + d.y)) {
                Tile tile = map.get(x + d.x, y + d.y);
                if(tile == this.tile) {
                    signature += d.bit;
                }
            } else {
                signature += d.bit;
            }
        }
    }

    /**
     * Checks that everything in the given mask is in the signature.
     * @param n,ne,e,se,s,sw,w,nw Flags denoting if there is an adjoining tile in that direction.
     * @return true if all the tiles marked by the mask are in the signature.
     */

    @SuppressWarnings({"JavaDoc"})
    public boolean signatureHas(boolean n, boolean ne, boolean e, boolean se, boolean s, boolean sw, boolean w, boolean nw) {
        int mask = 0;
        if(n)  mask += N;
        if(ne) mask += NE;
        if(e)  mask += E;
        if(se) mask += SE;
        if(s)  mask += S;
        if(sw) mask += SW;
        if(w)  mask += W;
        if(nw) mask += NW;

        return (mask & signature) == mask;
    }

    /**
     * Because we use this as a key in a hashtable we need to have equals and hashcode working well.
     * @param n The object we are being compared against.
     * @return  true if the objects are equal, otherwise false
     */
    public boolean equals(Object n) {
        if(!(n instanceof TileSignature)) return false;
        TileSignature that = (TileSignature)n;
        return (this.signature == that.signature) &&
               (this.tile == that.tile);
    }

    /**
     * Hashcodes must be equal if the objects are equal. More unique is better.
     * @return the hashcode of this signature.
     */
    public int hashCode() {
        if(tile == null) return signature + 42312;
        return signature + tile.hashCode();
    }
    
    public int getSignature() {
        return signature;
    }

    public Tile getTile() {
        return tile;
    }

    public String toString() {
        return "<Sig flags='" + Integer.toBinaryString(signature) + "'>";
    }
}
